"""
用于根据规模生成随机迷宫并保存
    1. 递归分割法生成迷宫
    2. 迷宫保存
    3. 放置道具，陷阱，BOSS等
    4.保存成json
    5. （想法）参考MC种子的思想，对生成的迷宫编号，
    可以选择输入种子选择要进入的迷宫（暂时不做）
"""
import os
import json
import random
from config import *
import numpy as np
from dp_planner_Dijkstra import DPPlannerDijkstra
from typing import List, Tuple, Set

class MazeManager(object):
    def __init__(self, difficulty=1, seed=0):
        self.size = MAZE_N_DIFFICULTY if difficulty == 1 else MAZE_N_SIMPLE
        self.grid = np.full((self.size, self.size), WALL, dtype=str)
    # BOSS 放置
    def _place_boss(self):
        dp_planner = DPPlannerDijkstra(self.grid)
        track_list, _ = dp_planner.calculate_optimal_path()

        for pos in reversed(track_list):
            r, c = pos[0], pos[1]
            if self.grid[r, c] == PATH:  # 假设 PATH 是表示路径的常量
                self.grid[r, c] = BOSS  # 假设 BOSS 是表示BOSS的常量
                break
        return

    def _recursive_division(self, r, c, height, width):
        if height < 3 or width < 3:
            return

        if height > width:
            orientation = 'horizontal'
        elif width > height:
            orientation = 'vertical'
        else:
            orientation = random.choice(['horizontal', 'vertical'])

        if orientation == 'horizontal':
            possible_wall_r = [i for i in range(r, r + height) if i % 2 == 0 and i != 0 and i != self.size - 1]
            if not possible_wall_r: return
            wall_r = random.choice(possible_wall_r)

            possible_passage_c = [i for i in range(c, c + width) if i % 2 != 0]
            if not possible_passage_c: return
            passage_c = random.choice(possible_passage_c)

            for i in range(c, c + width):
                self.grid[wall_r, i] = WALL
            self.grid[wall_r, passage_c] = PATH

            self._recursive_division(r, c, wall_r - r, width)
            self._recursive_division(wall_r + 1, c, r + height - (wall_r + 1), width)

        else:  # vertical
            possible_wall_c = [i for i in range(c, c + width) if i % 2 == 0 and i != 0 and i != self.size - 1]
            if not possible_wall_c: return
            wall_c = random.choice(possible_wall_c)

            possible_passage_r = [i for i in range(r, r + height) if i % 2 != 0]
            if not possible_passage_r: return
            passage_r = random.choice(possible_passage_r)

            for i in range(r, r + height):
                self.grid[i, wall_c] = WALL
            self.grid[passage_r, wall_c] = PATH

            self._recursive_division(r, c, height, wall_c - c)
            self._recursive_division(r, wall_c + 1, height, c + width - (wall_c + 1))
    # 放置除了BOSS的其他部分
    def _place_elements(self):
        path_coords = np.argwhere(self.grid == PATH).tolist()
        random.shuffle(path_coords)
        if not path_coords: return

        start_pos = path_coords.pop()
        exit_pos = None
        for pos in reversed(path_coords):
            dist = abs(pos[0] - start_pos[0]) + abs(pos[1] - start_pos[1])
            if dist > self.size / 2:
                exit_pos = pos
                path_coords.remove(pos)
                break
        if exit_pos is None: exit_pos = path_coords.pop()

        self.grid[start_pos[0], start_pos[1]] = START
        self.grid[exit_pos[0], exit_pos[1]] = EXIT

        elements_to_place = [(GOLD, NUM_GOLD), (TRAP, NUM_TRAPS), (LOCKER, NUM_LOCKERS)]
        for symbol, count in elements_to_place:
            for _ in range(count):
                if not path_coords: break
                pos = path_coords.pop()
                self.grid[pos[0], pos[1]] = symbol

    def generate(self):
        """
        通过调用迷宫生成，道具放置来产生完整迷宫并返回
        :return: 符合题目要求的迷宫
        """
        self.grid[1:self.size - 1, 1:self.size - 1] = PATH
        self._recursive_division(1, 1, self.size - 2, self.size - 2)
        self._place_elements()
        self._place_boss()
        return self.grid

    def save_to_json(self, filename="data/maze.json"):
        """
        :param filename: 文件保存的位置（默认main调用，所以是data）
        """
        os.makedirs(os.path.dirname(filename), exist_ok=True)
        with open(filename, "w") as f:
            json.dump(self.grid.tolist(), f, indent=2)
    # 找起点的方法
    def find_start(self):
        for r, row in enumerate(self.grid):
            for c, cell in enumerate(row):
                if cell == START:
                    return r, c
        return None # 没有起点
    # 找出口的方法
    def find_exit(self):
        for r, row in enumerate(self.grid):
            for c, cell in enumerate(row):
                if cell == EXIT:
                    return r, c
        return None # 没有出口
    # 使用DFS规划主路径
    def plan_main_path_dfs(self, start_pos: Tuple[int, int], exit_pos: Tuple[int, int]) -> List[Tuple[int, int]]:
        maze = self.grid
        rows = len(maze)
        cols = len(maze[0])
        moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]  # 上, 下, 左, 右
        visited: Set[Tuple[int, int]] = set()
        path: List[Tuple[int, int]] = []
        def _dfs(current_pos: Tuple[int, int]) -> bool:
            """
            嵌套的递归DFS函数。
            它能访问外部函数的变量如 'visited', 'path', 'exit_pos'等。
            """
            r, c = current_pos
            visited.add(current_pos)
            # 检查是否到达终点
            if current_pos == exit_pos:
                path.append(current_pos)
                return True
            # 探索邻居
            for dr, dc in moves:
                next_r, next_c = r + dr, c + dc
                # 检查移动是否有效
                if (0 <= next_r < rows and 0 <= next_c < cols and
                        maze[next_r][next_c] != '#' and
                        (next_r, next_c) not in visited):
                    # 如果从邻居节点递归下去能成功，则构建路径并返回
                    if _dfs((next_r, next_c)):
                        path.append(current_pos)
                        return True
            # 所有邻居都无法通向终点，此路不通
            return False
        # --- 4. 启动搜索并返回结果 ---
        if _dfs(start_pos):
            # 路径是反的（从终点到起点），需要反转
            return path[::-1]


# --- Test ---
if __name__ == '__main__':
    generator = MazeManager(difficulty=1)
    maze = generator.generate()
    generator.save_to_json()
    print(f"迷宫 ({generator.size}x{generator.size}) 已生成并保存到 data/maze.json")

    printable_maze = np.copy(maze)
    # printable_maze[printable_maze == ' '] = '.'
    for row in printable_maze:
        print(" ".join(list(row)))